package cn.mrcode.wxsdk.core.dialogue.hander;

import cn.mrcode.wxsdk.core.dialogue.protocol.msg.InMsg;
import cn.mrcode.wxsdk.core.dialogue.protocol.msg.in.*;
import cn.mrcode.wxsdk.core.dialogue.protocol.msg.in.event.*;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by zhuqiang on 2015/8/18.
 * 请求消息处理超类，该类 一般在 微信接入入口处调用，所以能很方便的替换实现类。<br/>
 * 该类有一个openTestMode()方法，用来打开测试模式。
 *
 * @T 用于扩展业务参数，该参数会随着参数回调处理对应的方法
 */
public abstract class InMessageProcessingHandler<T> {
    private static Logger log = LoggerFactory.getLogger(InMessageProcessingHandler.class);
    // 测试模式，为false：则把所有信息/事件都调用默认的处理方法，返回结果给公众号
    protected boolean testMode = false;

    /**
     * @param inMsg   请求消息
     * @param appId    用于支持多公众号处理（开发者账户id，或则是其他参数值，只要开发者自己能统一该参数代表的是什么）
     * @param paramObj 用于扩展业务参数，该参数会随着参数回调处理对应的方法
     *
     * @return
     */
    public String processing(InMsg inMsg, String appId, T paramObj) {
        InMsg msg = inMsg;

        String result = null;

        if (msg instanceof TextInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processTextMsg((TextInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof ImageInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processImageMsg((ImageInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof VoiceInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processVoiceMsg((VoiceInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof VideoInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processVideoMsg((VideoInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof ShortVideoInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processShortVideoMsg((ShortVideoInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof LocationInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processLocationMsg((LocationInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof LinkInMsg) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processLinkMsg((LinkInMsg) msg, appId, paramObj);
            }
        } else if (msg instanceof FollowInEvent) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processFollowEvent((FollowInEvent) msg, appId, paramObj);
            }
        } else if (msg instanceof QrCodeInEvent) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processQrCodeEvent((QrCodeInEvent) msg, appId, paramObj);
            }
        } else if (msg instanceof LocationInEvent) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processLocationEvent((LocationInEvent) msg, appId, paramObj);
            }
        } else if (msg instanceof MenuInEvent) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processMenuEvent((MenuInEvent) msg, appId, paramObj);
            }
        } else if (msg instanceof SpeechRecognitionResults) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processSpeechRecognitionResults((SpeechRecognitionResults) msg, appId, paramObj);
            }
        }else if (msg instanceof MenuLocationSelectInEvent) {
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processMenuLocationSelectEvent((MenuLocationSelectInEvent) msg, appId, paramObj);
            }
        }else if(msg instanceof TemplateSendJobFinishInEvent){
            if (testMode) {
                result = defaultProcess(msg, appId, paramObj);
            } else {
                result = processTemplateSendJobFinishInEvent((TemplateSendJobFinishInEvent) msg, appId, paramObj);
            }
        }
        else {
            log.error("未能识别的消息类型。 消息 转换为json 内容为：\n" + JSON.toJSONString(inMsg));
            result = defaultProcess(msg, appId, paramObj);
        }
        return result;
    }

    /**
     * 处理接收到的文本消息
     *
     * @param textInMsg
     * @param appId
     * @param paramObj
     *
     * @return
     */
    protected abstract String processTextMsg(TextInMsg textInMsg, String appId, T paramObj);

    /**
     * 处理接收到的图片消息
     *
     * @param imageInMsg
     * @param appId
     *
     * @return
     */
    protected abstract String processImageMsg(ImageInMsg imageInMsg, String appId, T paramObj);

    /**
     * 处理接收到的语音消息
     *
     * @param voiceInMsg
     * @param appId
     *
     * @return
     */
    protected abstract String processVoiceMsg(VoiceInMsg voiceInMsg, String appId, T paramObj);

    /**
     * 处理接收到的视频消息
     *
     * @param videoInMsg
     * @param appId
     *
     * @return
     */
    protected abstract String processVideoMsg(VideoInMsg videoInMsg, String appId, T paramObj);

    /**
     * 处理接收到的小视屏消息
     *
     * @param shortVideoInMsg
     * @param appId
     * @param paramObj
     *
     * @return
     */
    protected abstract String processShortVideoMsg(ShortVideoInMsg shortVideoInMsg, String appId, T paramObj);

    /**
     * 处理接收到的地址位置消息
     *
     * @param locationInMsg
     * @param appId
     *
     * @return
     */
    protected abstract String processLocationMsg(LocationInMsg locationInMsg, String appId, T paramObj);

    /**
     * 处理接收到的链接消息
     *
     * @param linkInMsg
     * @param appId
     *
     * @return
     */
    protected abstract String processLinkMsg(LinkInMsg linkInMsg, String appId, T paramObj);

    /**
     * 处理接收到的关注/取消关注事件
     *
     * @param followInEvent
     * @param appId
     *
     * @return
     */
    protected abstract String processFollowEvent(FollowInEvent followInEvent, String appId, T paramObj);

    /**
     * 处理接收到的关注/取消关注事件
     * 扫描带参数二维码事件

         用户扫描带场景值二维码时，可能推送以下两种事件：

         1、如果用户还未关注公众号，则用户可以关注公众号，关注后微信会将带场景值关注事件推送给开发者。
         2、如果用户已经关注公众号，则微信会将带场景值扫描事件推送给开发者。
     *
     * @param followInEvent
     * @param appId
     *
     * @return
     */
    protected abstract String processSubscribeEvent(FollowInEvent followInEvent, String appId, T paramObj);

    /**
     * 处理接收到的扫描带参数二维码事件
     *
     * @param qrCodeInEvent
     * @param appId
     *
     * @return
     */
    protected abstract String processQrCodeEvent(QrCodeInEvent qrCodeInEvent, String appId, T paramObj);

    /**
     * 处理接收到的上报地理位置事件
     *
     * @param locationInEvent
     * @param appId
     *
     * @return
     */
    protected abstract String processLocationEvent(LocationInEvent locationInEvent, String appId, T paramObj);

    /**
     * 处理接收到的自定义菜单事件
     *
     * @param menuInEvent
     * @param appId
     *
     * @return
     */
    protected abstract String processMenuEvent(MenuInEvent menuInEvent, String appId, T paramObj);

    /**
     * 处理接收到的 自定义菜单事件8：弹出地理位置选择器的事件推送
     * @param menuLocationSelectInEvent
     * @param appId
     * @param paramObj
     * @return
     */
    protected abstract String processMenuLocationSelectEvent(MenuLocationSelectInEvent menuLocationSelectInEvent,String appId, T paramObj);

    /**
     * 处理 发送模版消息之后的 上报事件
     * @param msg
     * @param appId
     * @param paramObj
     * @return
     */
    protected abstract String processTemplateSendJobFinishInEvent(TemplateSendJobFinishInEvent msg, String appId, T paramObj);

    /**
     * 处理接收到的语音识别结果
     *
     * @param speechRecognitionResults
     * @param appId
     *
     * @return
     */
    protected abstract String processSpeechRecognitionResults(SpeechRecognitionResults speechRecognitionResults, String appId, T paramObj);

    /**
     * 处理无法识别的消息，返回默认回复消息
     *
     * @param inMsg
     * @param appId
     *
     * @return
     */
    protected abstract String defaultProcess(InMsg inMsg, String appId, T paramObj);

    public boolean isTestMode() {
        return testMode;
    }

    /**
     * 打开测试模式，所有的消息处理都调用默认消息处理：默认为true
     */
    public void openTestMode() {
        this.testMode = true;
    }

    /**
     * 关闭测试模式
     */
    public void closeTestMode() {
        this.testMode = false;
    }
}
